/* 
    8.3继承
        继承是面向对象编程中讨论最多的话题。很多面向对象语言都支持两种继承：接口继承和实现继承。
        前者只继承方法签名，后者继承实际的方法。接口继承在ECMAScript中是不可能的，因为函数没有签
        名。实现继承是ECMAScript唯一支持的继承方式，而这主要是通过原型链实现的。
        --理解，比如java，接口继承更多是一种规范，不继承具体的实现。实现继承是像java里的类的继承，类里面已经写好的方法直接继承拿来用。
                在js中无法做接口继承，因为接口继承只继承方法签名（这段的解释是js是动态类型语言，没有内置的接口机制，最直接的原因有三点
                    ①无编译时类型检查，js在运行是才会做代码检查是否存在，无法在编写阶段做强制约束
                    ②无interface关键字：只有在es6的时候才加了类，一直没有接口。
                    ③鸭子类型主导：js推崇如果它走起来像鸭子，叫起来像鸭子，那它就是鸭子，关注实际行动而非静态类型。
                ）
                在TS中，针对 ​​类型系统​​ 和 ​​面向对象编程​​ 进行了显著增强，尤其是在接口（Interface）和继承方面。
                ts添加了interface，可在编译时对类型进行检查，强制实现类必须满足接口签名，否则编译报错。
                
    8.3.1原型链
        ECMA-262把原型链定义为ECMAScript的主要继承方式。其基本思想就是通过原型继承多个引用
        类型的属性和方法。重温一下构造函数、原型和实例的关系：每个构造函数都有一个原型对象，原型有
        一个属性指回构造函数，而实例有一个内部指针指向原型。如果原型是另一个类型的实例呢？那就意味
        着这个原型本身有一个内部指针指向另一个原型，相应地另一个原型也有一个指针指向另一个构造函
        数。这样就在实例和原型之间构造了一条原型链。这就是原型链的基本构想。 

        对于原型链的理解：假设一个实例对象是另一个对象的原型，而这个实例对象也有自己的原型对象，因此形成了一个原型链
        对于属性的读取，先从实例中读取，若读取不到去其原型对象中读取，若其原型对象中也没有，去其原型对象的原型对象中读取，直到读取object的原型对象null

        1.默认原型
            所有引用类型都继承自Object，这也是通过原型链实现的。
        2.原型与继承关系
            原型与实例的关系可以通过两种方式来确定。一种是instanceof()，第二种方式是使用isPrototypeOf()方法
        3.关于方法

        4.原型链的问题
            ①主要问题出现在原型中包含引用值的时候。
            ②原型链的第二个问题是，子类型在实例化时不能给父类型的构造函数传参。

    8.3.2盗用构造函数
        为了解决原型包含引用值导致的继承问题，一种叫做"盗用构造函数"的技术，也叫对象伪装、经典继承。
        基本思路：在子类构造函数中调用父类构造函数。因为毕竟函数就是在特定上下文中执行代码的简单对象，所以可以使用
        apply()和 call()方法以新创建的对象为上下文执行构造函数。
        1. 传递参数 
        相比于使用原型链，盗用构造函数的一个优点就是可以在子类构造函数中向父类构造函数传参。
        2.盗用构造函数的问题
        盗用构造函数的主要缺点，也是使用构造函数模式自定义类型的问题：必须在构造函数中定义方法，因此函数不能重用。
        子类也无法访问父类的原型上定义的方法，所有类型只能使用构造函数模式。
    8.3.3 组合继承
        组合继承（ 伪经典继承）综合了原型链和盗用构造函数。
        组合继承弥补了原型链和盗用构造函数的不足，是JavaScript中使用最多的继承模式。
    8.3.4 原型式继承
        他的出发点是即使不自定义类型也可以通过原型实现对象之间的信息共享。这个object()函数会创建一个临时构造函数，将传入的对象赋值给这个构造函数的原型，然后返
        回这个临时类型的一个实例。本质上，object()是对传入的对象执行了一次浅复制。
        ECMAScript 5 通过增加Object.create()方法将原型式继承的概念规范化了,这个方法接收两个参数：作为新对象原型的对象，以及给新对象定义额外属性的对象（第二个可选）。
        在只有一个参数时,Object.create()与这里的object()方法效果相同.
    8.3.5 寄生式继承
        创建一个实现继承的函数，以某种方式增强对象，然后返回这个对象。
    8.3.6 寄生式组合继承
        组合继承其实也存在效率问题。最主要的效率问题就是父类构造函数始终会被调用两次
        寄生式组合继承可以算是引用类型继承的最佳模式。
*/
// 盗用构造函数
// function SuperType() {
//     this.colors = ['1','2','3']
// }
// function SubType(){
//     SuperType.apply(this)
// }
// let instance1 = new SubType()
// instance1.colors.push('4')
// console.log(instance1.colors)

// let instance2 = new SubType()
// console.log(instance2.colors)

// 传递参数
// function SuperType(name){
//     this.name = name
// }
// function SubType(){
//     SuperType.call(this,"Nic")
//     this.age = 29
// }
// let instance = new SubType()
// console.log(instance.name)
// console.log(instance.age)

// 组合继承
// function SuperType(name){
//     this.name = name;
//     this.colors = ['1','2','3']
// }
// SuperType.prototype.sayName = function(){
//     console.log(this.name)
// }
// function SubType(name,age){
//     // 继承属性
//     SuperType.call(this,name)
//     this.age = age
// }

// SubType.prototype = new SuperType()
// SubType.prototype.sayAge = function(){
//     console.log(this.age)
// }

// let instance1 = new SubType("Nic",29)
// instance1.colors.push('black')
// console.log(instance1.colors)
// instance1.sayAge()
// instance1.sayName()

// let instance2 = new SubType("Gre",27)
// console.log(instance2.colors)
// instance2.sayAge()
// instance2.sayName()

// 原型式继承
// function object(o){
//     function F(){}
//     F.prototype = o
//     return new F()
// }

// let person = {
//     name:"",
//     friends:["shelby","Court","Van"]
// }

// let anotherPerson = object(person)
// anotherPerson.name = "Greg"
// anotherPerson.friends.push("Rob")

// let yetAnotherPerson = object(person)
// yetAnotherPerson.name = "Linda"
// yetAnotherPerson.friends.push("Barbie")

// console.log(person.friends)

// 原型式继承使用Object.create
// let person = {
//     name:"",
//     friends:['shelby',"court","van"]
// }
// let anotherPerson = Object.create(person)
// anotherPerson.name = "greg"
// anotherPerson.friends.push('rob')

// let yetAnotherPerson = Object.create(person)
// yetAnotherPerson.name = "linda"
// yetAnotherPerson.friends.push("barbie")

// console.log(person.friends)

// let p1 = Object.create(person,{name:{value:"Greg"}})

// console.log(p1.name)

// 8.3.5寄生式继承
// function object(o){
//     function F(){}
//     F.prototype = o
//     return new F()
// }
// function createAnother (original){
//     let clone = object(original)
//     clone.sayHi = function(){
//         console.log("hi")
//     }
//     return clone
// }
// let person = {
//     name:"nic",
//     friends:['Shelby','Court','Van']
// }
// let anotherPerson = createAnother(person)
// anotherPerson.sayHi()

// 8.3.6 寄生式组合继承
function SuperType (name){
    this.name = name
    this.colors=["1","2","3"]
}
SuperType.prototype.sayName = function(){
    console.log(this.name)
}

function SubType(name,age){
    SuperType.call(this,name)  //第二次调用SuperType
    this.age = age
}
SubType.prototype = new SuperType() //第一次调用SuperType
SubType.prototype.constructor = SubType
SubType.prototype.sayAge = function(){
    console.log(this.age)
}
